package decode;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import client.Client;

import file.BufferMap;
import file.File;

public class DecodeMessage {

	private static final long _DEFAULT_KEY_LENGTH = 32;
	private static final String shellCode = "(\\x00|\\x01|\\x02|\\x03|\\x04|\\x05|\\x06|\\x07|\\x08|\\x09|\\x0a|\\x0b|\\x0c|\\x0d|\\x0e|\\x0f|\\x10|\\x11|\\x12|\\x13|\\x14|\\x15|\\x16|\\x17|\\x18|\\x19|\\x1a|\\x1b|\\x1c|\\x1d|\\x1e|\\x1f|\\x20|\\x21|\\x22|\\x23|\\x24|\\x25|\\x26|\\x27|\\x28|\\x29|\\x2a|\\x2b|\\x2c|\\x2d|\\x2e|\\x2f|\\x30|\\x31|\\x32|\\x33|\\x34|\\x35|\\x36|\\x37|\\x38|\\x39|\\x3a|\\x3b|\\x3c|\\x3d|\\x3e|\\x3f|\\x40|\\x41|\\x42|\\x43|\\x44|\\x45|\\x46|\\x47|\\x48|\\x49|\\x4a|\\x4b|\\x4c|\\x4d|\\x4e|\\x4f|\\x50|\\x51|\\x52|\\x53|\\x54|\\x55|\\x56|\\x57|\\x58|\\x59|\\x5a|\\x5b|\\x5c|\\x5d|\\x5e|\\x5f|\\x60|\\x61|\\x62|\\x63|\\x64|\\x65|\\x66|\\x67|\\x68|\\x69|\\x6a|\\x6b|\\x6c|\\x6d|\\x6e|\\x6f|\\x70|\\x71|\\x72|\\x73|\\x74|\\x75|\\x76|\\x77|\\x78|\\x79|\\x7a|\\x7b|\\x7c|\\x7d|\\x7e|\\x7f|\\x80|\\x81|\\x82|\\x83|\\x84|\\x85|\\x86|\\x87|\\x88|\\x89|\\x8a|\\x8b|\\x8c|\\x8d|\\x8e|\\x8f|\\x90|\\x91|\\x92|\\x93|\\x94|\\x95|\\x96|\\x97|\\x98|\\x99|\\x9a|\\x9b|\\x9c|\\x9d|\\x9e|\\x9f|\\xa0|\\xa1|\\xa2|\\xa3|\\xa4|\\xa5|\\xa6|\\xa7|\\xa8|\\xa9|\\xaa|\\xab|\\xac|\\xad|\\xae|\\xaf|\\xb0|\\xb1|\\xb2|\\xb3|\\xb4|\\xb5|\\xb6|\\xb7|\\xb8|\\xb9|\\xba|\\xbb|\\xbc|\\xbd|\\xbe|\\xbf|\\xc0|\\xc1|\\xc2|\\xc3|\\xc4|\\xc5|\\xc6|\\xc7|\\xc8|\\xc9|\\xca|\\xcb|\\xcc|\\xcd|\\xce|\\xcf|\\xd0|\\xd1|\\xd2|\\xd3|\\xd4|\\xd5|\\xd6|\\xd7|\\xd8|\\xd9|\\xda|\\xdb|\\xdc|\\xdd|\\xde|\\xdf|\\xe0|\\xe1|\\xe2|\\xe3|\\xe4|\\xe5|\\xe6|\\xe7|\\xe8|\\xe9|\\xea|\\xeb|\\xec|\\xed|\\xee|\\xef|\\xf0|\\xf1|\\xf2|\\xf3|\\xf4|\\xf5|\\xf6|\\xf7|\\xf8|\\xf9|\\xfa|\\xfb|\\xfc|\\xfd|\\xfe|\\xff)";
	public static Client _client;
	/**
	 * Fonction permettant de décoder un message prennant en compte le message framing
	 * @param input
	 * @param type
	 * @return une string correspondant au message décodé ou null
	 * @throws Exception
	 */
	public static String decodeMessageFromSocket(InputStream input, String type, File f) throws Exception{
		String keyLength = (f != null) ? "{"+f.getIdFile().length()+"}" : "{"+_DEFAULT_KEY_LENGTH+"}";
		String bufferMapLength = (f != null) ? "{"+f.getBufferMap().getLength()+"}" : "{8}";
		//String pieceLength = (f != null) ? "{"+f.getPieceSize()+"}" : "1024";
		//TODO taille de pièce pour les data
		String[] regexpTracker = {"^ok", "^list \\[.*\\]", "^peers [a-f0-9]"+keyLength+" \\[.*\\]"};
		String[] regexpClient = {"^have [a-f0-9]"+keyLength+" "+shellCode+bufferMapLength, "^data [a-f0-9]"+keyLength+" \\[([0-9]+:"+shellCode+"*.?)+\\]"};
		String[] regexpClientServer = {"^interested [a-f0-9]"+keyLength, "^getpieces [a-f0-9]"+keyLength+" \\[([0-9]+( )*)+\\]", "^have [a-f0-9]"+keyLength+" "+shellCode+bufferMapLength};
		/*String[] regexpError = {	"^[^(o|l|p|h|d|i|g)]",
									"^[^(ok|li|pe|ha|da|in|ge)]",
									"^[^(lis|pee|hav|dat|int|get)]",
									"^[^(list|peer|have|data|inte|getp)]",
									"^[^(peers|inter|getpi)]",
									"^[^(intere|getpie)]",
									"^[^(interes|getpiec)]",
									"^[^(interest|getpiece)]",
									"^[^(intereste|getpieces)]",
									"^[^(interested)]"
								};*/

		boolean decoded = false;
		boolean error = true;
		StringBuffer buff = new StringBuffer();
		int c;
		String[] regexp;
		if(type.equals("client")){
			regexp = regexpClient;
		}
		else if(type.equals("clientServer")){
			regexp = regexpClientServer;
		}
		else if(type.equals("tracker")){
			regexp = regexpTracker;
		}
		else{
			throw new Exception("Type undefined");
		}
		while(!decoded){
			c = input.read();
			if(c == -1){
				return buff.toString();
			}
			else{
				buff.append( (char) c);
				
				for(int j = 0; j < regexp.length; j++){
					String splited = regexp[j].split("\\^")[1];
					splited = splited.split(" ")[0];
				
					if(buff.toString().length() + 1 <= splited.length()){
						
						if(buff.toString().regionMatches(0, splited, 0, buff.toString().length())){
							error = false;
						}
					}
				}
				if(error){
					System.err.println("No regexp starting with "+ buff.toString());
					return null;
				}
				
				
				for(int i = 0; i < regexp.length; i++){
					
					if(buff.toString().matches("^have [a-f0-9]"+keyLength+" ")){
						if(f == null){
							int k = 0;
							ArrayList<File> af = new ArrayList<File>(_client.getMySeed());
							af.addAll(_client.getMyLeech());
							while(k < af.size() && !af.get(k).getIdFile().equals(buff.toString().split(" ")[1]))
								k++;
							f = af.get(k);
							_client = null;
						}
					
						byte[] b = new byte[(int) Math.ceil(f.getBufferMap().getLength()/8.0)];
						for(int j = 0; j < (int) Math.ceil(f.getBufferMap().getLength()/8.0); j++){
							//System.err.println("INFO : j = "+ j);
							c = input.read(b, j, 1);
							//byteBuffer.put(j, b[0]);
							//System.err.println("BYTEBUFFER : "+ b);
						}
						decoded = true;
						String a = new String(buff.toString().getBytes("ISO-8859-1"), "ISO-8859-1");
						a += new String(b, "ISO-8859-1");
						//System.err.println("INFO : RETURNING : " + a);
					
						return a;
					}
					if(buff.toString().matches("^data [a-f0-9]"+keyLength+" \\[")){
						
						if(String.valueOf(c).equals(":")){
							byte[] b = new byte[(int) f.getPieceSize()];
							for(int j = 0; j < f.getPieceSize(); j++){
								
								c = input.read(b, j, 1);
								
							}
							String a = new String(b, "ISO-8859-1");
							buff.append(a);
						}
					}
					if(buff.toString().trim().matches(regexp[i])){
						decoded = true;
						return buff.toString().trim();
					}
					//TODO la même pour les datas
				}
			}
		}
		return null;
	}

	public static String decodeMessageFromSocket(InputStream input, String type) throws Exception{
		String str = decodeMessageFromSocket(input, type, null);
		return str;
	}
}
